package com.warm.base.jdk.thread.base;

/**
 * @Date: 2020/3/30 14:04
 * @Author: LChuang
 * @Description:
 *  ThreadLocal 用在多线程场景下
 *
 *  用途：
 *      1. 保存线程上下文信息, 在任意需要地方可以获取
 *         由于ThreadLocal的特性，同一线程在某地方进行设置，在随后的任意地方都可以获取到。
 *
 *      2. 线程安全, 避免了某些情况下需要考虑线程安全必须同步带来的性能损失
 *        (每个线程往 ThreadLocal 中读写数据是线程隔离, 相互不影响, 无法解决共享对象的更新问题)
 *
 *  Thread 类有属性变量threadLocals(ThreadLocalMap), 每个线程都有一个自己的ThreadLocalMap,
 *      所以每个线程往这个ThreadLocalMap中读写隔离, 不会影响
 *  一个ThreadLocal只能存储一个Object对象, 如果需存储多个Object就需要多个ThreadLocal
 *
 *  Thread.threadLocals == ThreadLocalMap :
 *      { Entry{ key: ThreadLocal, value: Object }, Entry{}... }
 *      Entry.key 指向 ThreadLocal, ThreadLocal会进行回收 :(Entry extends WeakReference 弱引用)
 *
 *  最佳实践: ThreadLocal被垃圾回收后,在ThreadLocalMap里对应的Entry的键值会变成null,
 *          在我们不使用的时候,主动调用remove方法进行清理 : {threadLocal.remove()}
 */
public class TestThreadLocal {

    /**
     * ThreadLocal 不支持继承性：同一个 ThreadLocal 变量在父线程中设置后，子线程访问不到
     */
    private static ThreadLocal<Integer> intThreadLocal = new ThreadLocal<>();
    private static ThreadLocal<String> strThreadLocal = new ThreadLocal<>();

    /**
     * 使用 InheritableThreadLocal 可以使子线程获取父线程成员变量的值
     */
    private static InheritableThreadLocal<String> stringInheritableThreadLocal = new InheritableThreadLocal<>();

    public static void main(String[] args) {

        try {
            strThreadLocal.set("String ThreadLocal");
            stringInheritableThreadLocal.set("String InheritableThreadLocal");
            new Thread(() -> {
                try{
                    intThreadLocal.set(1);

                    Thread.sleep(30000); // 设置睡眠，为了测试线程2 是否能拿到 intThreadLocal 的值

                    System.out.println(Thread.currentThread().getName() + "==== Int ====" + intThreadLocal.get());
                    System.out.println(Thread.currentThread().getName() + "==== Str ====" + strThreadLocal.get());

                    System.out.println(Thread.currentThread().getName() + "==== Str Inherit ====" + stringInheritableThreadLocal.get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    intThreadLocal.remove(); // 当前线程使用完记得移除
                }
            }, "thread-A").start();

            new Thread(() -> {
                intThreadLocal.get();

                System.out.println(Thread.currentThread().getName() + "==== Int ====" + intThreadLocal.get());
                System.out.println(Thread.currentThread().getName() + "==== Str ====" + strThreadLocal.get());

                System.out.println(Thread.currentThread().getName() + "==== Str Inherit ====" + stringInheritableThreadLocal.get());
            }, "thread-B").start();

            System.out.println(Thread.currentThread().getName() + "==== Int ====" + intThreadLocal.get());
            System.out.println(Thread.currentThread().getName() + "==== Str ====" + strThreadLocal.get());

            System.out.println(Thread.currentThread().getName() + "==== Str Inherit ====" + stringInheritableThreadLocal.get());
        } finally {
            strThreadLocal.remove();
            stringInheritableThreadLocal.remove();
        }
    }
}
